#include <algorithm>
#include <iostream>
#include "path.hpp"

//using namespace boost;
using namespace std;

Path::Path() {
	firstAlignment_ = PSequence(new Sequencee);
	secondAlignment_ = PSequence(new Sequencee);
}

Path::~Path() {
}

void Path::findMaxValue(const PMatrixType matrix) {
	if(matrix == NULL)
		throw logic_error("No matrix in: Path::findMaxValue");

	ArrayType tmpMaxElement;
	ArrayType colIndex;

	ConstItr2D rowBeginItr = matrix->begin();
	ConstItr2D rowEndItr = matrix->end();

	ConstItr1D tmpItr;
	for(ConstItr2D i = rowBeginItr; i != rowEndItr; ++i) {
		ConstItr1D colBeginItr = i->begin();
		ConstItr1D colEndItr = i->end();

		tmpItr = max_element(colBeginItr, colEndItr);

		tmpMaxElement.push_back(*tmpItr);
		colIndex.push_back(tmpItr - colBeginItr);
	}
	
	ConstItr1D vItr = max_element(tmpMaxElement.begin(), tmpMaxElement.end());

	ArrayType tmp;
	tmp.push_back(vItr - tmpMaxElement.begin());
	tmp.push_back(colIndex[tmp[0]]);
	
	path_->push_back(tmp);
}

void Path::findPath(const PMatrix matrix, const int end_value) {
	if(matrix == NULL)
		throw logic_error("No matrix in: Path::findPath");

	path_.reset();
	path_ = PMatrixType(new MatrixType);

	PMatrixType valuesMatrix = matrix->getMatrix();
	PMatrixType pathMatrix = matrix->getPathMatrix();

	findMaxValue(valuesMatrix);

	int row = (*path_)[0][0];
	int col = (*path_)[0][1];
	ArrayType tmp;
	
	for(int i = 1; row != 0 && col != 0; ++i) {
		tmp.clear();

		if((*pathMatrix)[row][col] == 0) {
			tmp.push_back(--row);
			tmp.push_back(--col);
			path_->push_back(tmp);	// idzie w gore i lewo
		}
		else if((*pathMatrix)[row][col] == 2) {
			tmp.push_back(--row);
			tmp.push_back(col);
			path_->push_back(tmp);	// idzie w gore
		}
		else if((*pathMatrix)[row][col] == 1) {
			tmp.push_back(row);
			tmp.push_back(--col);
			path_->push_back(tmp);	// idzie w lewo
		}
		else {
			throw logic_error("Invalid value in: Path::findPath"); 
		}

		if((*valuesMatrix)[row][col] <= end_value)
				return;
	}
}

void Path::reconstructAlignment(const PSequence seq1, const PSequence seq2) {
	if (seq1 == NULL || seq2 == NULL)
		throw logic_error("No sequence available in: Path::reconstructAlignment");

	int i = 1;
	std::string tmpAlig1, tmpAlig2;

	firstAlignment_->clearSequence();
	secondAlignment_->clearSequence();

	string::iterator firstItr = tmpAlig1.begin();
	string::iterator secondItr = tmpAlig2.begin();

	tmpAlig1.insert(firstItr, seq1->getSequenceElement((*path_)[i][0]));
	tmpAlig2.insert(secondItr, seq2->getSequenceElement((*path_)[i][1]));
	
	int endPath = path_->size();
	for(int i = 2; i < endPath; ++i) {
		firstItr = tmpAlig1.begin();
		secondItr = tmpAlig2.begin();

		if((*path_)[i-1][0] == (*path_)[i][0]) {
			tmpAlig1.insert(firstItr, '-');
			tmpAlig2.insert(secondItr, seq2->getSequenceElement((*path_)[i][1]));
		}
		else if((*path_)[i-1][1] == (*path_)[i][1]) {
			tmpAlig1.insert(firstItr, seq1->getSequenceElement((*path_)[i][0]));
			tmpAlig2.insert(secondItr, '-');
		}
		else {
			tmpAlig1.insert(firstItr, seq1->getSequenceElement((*path_)[i][0]));
			tmpAlig2.insert(secondItr, seq2->getSequenceElement((*path_)[i][1]));
		}
	}

	firstAlignment_->setSequence(tmpAlig1);
	secondAlignment_->setSequence(tmpAlig2);

	return;
}
